DML触发器
兼容 ORACLE DML 触发器语法须 SQLMODE 在 ORACLE 兼容模式下。
set environment sqlmode ‘oracle’;
O 兼容 DML 触发器(TRIGGER)定义为当某些数据操作事件发生时,数据库应该采取的操作。在相关的事件发生时,由数据库自动地隐式地激发语句集合,即直到一个语句激发的所有触发器执行完成之后该语句才结束,而其中任何一个触发器执行的失败都将导致该语句的失败。 O 兼容 DML 触发器与 O 兼容存储模块类似,都是在数据库上保存并执行的一段 PLSQL 语句。当触发器被触发时,数据库将执行保存的 PLSQL 语句。
当前版本以不同模式区分原生触发器与 Oracle 兼容触发器。 O 兼容触发器在 gbase8s 模式下触发,数据库将返回报错。因此,为保证行为一致,涉及目标表的DML操作也需在相同模式下进行。例如,在 Oracle 模式下创建的触发器需保持在 Oracle 模式下对目标表进行INSERT、UPDATE 或 DELETE操作。如果在 gbase8s 模式下对目标表进行DML操作,则报错,但由于触发器的机制逻辑,原则上触发器报错不影响DML操作本身。
触发器的创建
使用 CREATE TRIGGER 语句在表上定义触发器。
创建触发器语法图如下:
simple_dml_trigger ::=
dml_event_clause ::=
referencing_clause ::=
元素 | 描述 | 限制 |
---|---|---|
database | 所在数据库 | 需要对应数据库的对应操作权限 |
trigger_name | 新的触发器声明的名称 | 必须在当前数据库中的触发 器名称中是唯一的 |
condition | 触发必须满足的逻辑条件 | 必须在行级触发器下使用,即必须与 FOR EACH ROW 一同使用 |
trigger_body | 触发器被激活时执行的PLSQL语句块 | 必须在 Oracle 模式下 |
column | 激活触发器的列 | 必须在触发 table 中存在 |
table | 触发表的名称 | 必须在数据库中存在 |
old | 在触发器操作中使用的旧引用声明的名称 | 在此 CREATE TRIGGER 语句中必须唯一 |
new | 在触发器操作中使用的新引用声明的名称 | 在此 CREATE TRIGGER 语句中必须唯一 |
触发器相关概念说明:
CREATE TRIGGER <触发器名称>
<触发时间> <触发事件> ON <触发对象> [<触发级别>]
<触发模式>
<触发条件>
<触发体>;
- 触发事件:引起触发器被触发的DML 语句事件,包括 INSERT 、DELETE 、UPDATE 。
- 触发时间:触发事件和该 TRIGGER 的操作顺序。 BEFORE 指明触发器在执行触发语句之前激发;AFTER 指明触发器在执行触发语句之后激发。
- 触发操作/触发体:即该 TRIGGER 被触发之后要执行的 PLSQL 语句块。
- 触发对象:被定义触发器的数据库对象,当前版本仅支持基表。
- 触发级别:即语句级触发器和行级触发器。
- 语句级触发器:是指当某触发事件发生时,该触发器只执行一次;
- 行级触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次,使用 FOR EACH ROW 定义。
- 触发条件:由WHEN子句指定一个逻辑表达式。必须在行级触发器下使用,即必须与 FOR EACH ROW 一同使用。只有当该表达式的值为TRUE时,遇到触发事件才会自动执行触发器,使其执行触发操作。
- 触发模式:设置触发器为启用(ENABLE)或禁用(DISABLE)。
存在如下限制:
- 指定 OR REPLACE 将创建一个新触发器或替换同名的现有触发器。
- 创建触发器的表必须存在于当前数据库中。
- 不支持在诊断表、违例表、外部数据库中的表、临时表、外部表、系统目录表创建触发器。
例如,表 tab 1 表结构如下:
CREATE TABLE tab1 (C1 int ,c2 varchar(20));
CREATE TABLE tab2 (c1 int, c2 varchar(20));
插入如下数据:
INSERT INTO tab1 values(1,'test');
INSERT INTO tab1 values(2,'test');
例子1,在表 tab1 上创建 BEFORE 触发器,插入 2 行数据至表 tab1 ,触发器 tri1 将被激发 1 次:
CREATE OR REPLACE TRIGGER tri1 BEFORE INSERT ON tab1
BEGIN
INSERT INTO tab2 values(null,'test');
END;
/
-----批插 2 条数据
INSERT INTO tab1 SELECT * FROM tab1;
-----触发 1 次
SELECT * FROM tab2;
C1|C2 |
--+----+
|test|
例子2,在表 tab1 上创建 AFTER 触发器,插入 2 行数据至表 tab1 ,触发器 tri2 将被激发一次:
CREATE OR REPLACE TRIGGER tri2 AFTER INSERT ON tab1
BEGIN
INSERT INTO tab2 values(null,'test');
END;
/
-----批插 2 条数据
INSERT INTO tab1 SELECT * FROM tab1;
-----触发 1 次
SELECT * FROM tab2;
C1|C2 |
--+----+
|test|
例子3,在表 tab1 上创建 BEFORE触发器,触发事件为 INSERT 、 UPDATE 或DELETE ,分别执行插入、更新、删除,触发器 tri3 被激发 3 次:
CREATE OR REPLACE TRIGGER tri3 BEFORE INSERT OR UPDATE OR DELETE ON tab1
BEGIN
INSERT INTO tab2 values(null,'test');
END;
/
-----插入
INSERT INTO tab1 SELECT * FROM tab1;
-----更新
UPDATE tab1 SET c2='test1' WHERE c1 = 1;
-----删除
DELETE tab1 WHERE c1 = 1;
-----触发 3 次
SELECT * FROM tab2;
C1|C2 |
--+----+
|test|
|test|
|test|
例子4,在表 tab1 上创建 BEFORE FOR EACH ROW 行级触发器,插入 2 行数据至表 tab1 ,触发器 tri4 将被激发 2 次:
CREATE OR REPLACE TRIGGER tri4 BEFORE INSERT ON tab1 FOR EACH ROW
BEGIN
INSERT INTO tab2 values(null,'test')
END;
/
-----批插 2 条数据
INSERT INTO tab1 SELECT * FROM tab1;
-----触发 2 次
SELECT * FROM tab2;
C1|C2 |
--+----+
|test|
|test|
例子5,在表 tab1 上创建 BEFORE FOR EACH ROW 行级触发器,定义该触发器启用,并定义触发条件为 :NEW.c1>10 ,插入c1 值为10与11的 2 行数据,触发器 tri5 将被激发 1 次:
CREATE OR REPLACE TRIGGER tri5 BEFORE INSERT ON tab1 FOR EACH ROW ENABLE WHEN (NEW.c1 >10)
BEGIN
INSERT INTO tab2 values(null,'test');
END;
/
-----批插 2 条数据
INSERT INTO tab1 VALUES(10,'test');
INSERT INTO tab1 VALUES(11,'test');
-----触发 1 次
SELECT * FROM tab2;
C1|C2 |
--+----+
|test|
启用或禁用触发器
使用 ALTER TRIGGER 语句来启用或禁用表上的触发器。
- ENABLE 表示,如果发生触发事件,则数据库服务器执行触发操作。
- DISABLE 表示,触发事件不会导致执行触发操作,数据库服务器将忽略该触发器及其操作。
启用或禁用触发器语法图如下:
元素 | 描述 | 限制 |
---|---|---|
database | 所在数据库 | 需要对应数据库的对应操作权限 |
trigger_name | 新的触发器声明的名称 | 必须在当前数据库中的触发 器名称中是唯一的 |
例如,启用触发器 tri1 :
ALTER TRIGGER tri1 ENABLE;
触发体
O 兼容 DML 触发器触发体定义基于 PLSQL 语法。
CREATE TRIGGER <触发器名称> <触发时间> <触发事件> ON <触发对象> [<触发级别>] <触发模式> <触发条件> <触发体>;
<触发体> ::=
[ DECLARE <声明部分>]
BEGIN
<执行部分>
END [触发器名称];
当前版本已实现的 PLSQL 语法在触发体部分均支持。PLSQL特性详细边界描述及用例,详见本手册对应章节。
新、旧行值的引用
仅在行级触发器,可以使用新、旧行值的引用访问正在处理的行中的数据。缺省的引用名是 :OLD、:NEW,可使用 REFERENCING 子句修改。:OLD 表示记录被处理前的值,:NEW 表示记录被处理后的值。
在触发体内,使用以下语法引用新旧行的字段:
:引用名.列名
:OLD、:NEW 不同触发事件下含义如下:
触发事件 | :OLD | :NEW |
---|---|---|
INSERT | 报错处理 | 该语句结束时将插入的值 |
UPDATE | 更新前的旧值 | 该语句结束时将更新的值 |
DELETE | 删除前的旧值 | 报错处理 |
说明及限制:
- 新、旧数据的引用,仅当触发器为行级触发器可使用,即使用 FOE EACH ROW 定义触发级别。当触发器为语句级时,单次触发可能涉及多行数据,系统无法定位新、旧数据的引用如何指向。
- 省略 REFERENCING 子句,缺省引用为 :OLD、:NEW。
- 引用不能出现在 RECORD 级的相关赋值操作中,需以 :引用名.字段 的形式使用。例如, :NEW := NULL 是不允许的。
- 触发器不能更改:OLD的字段值。
- BEFORE 触发器可以在触发事件INSERT将 :NEW 字段值放入表中之前更改它们。 AFTER 触发器不能更改 :NEW字段值,因为触发语句在触发器触发之前运行。
- 如果同时定义了 BEFORE INSERT 触发器和 AFTER INSERT 触发器,并且 BEFORE INSERT 触发器更改了 :NEW 字段值,则 AFTER INSERT 触发器可见该更改。
- WHEN 子句使用新旧引用,引用名依据 REFERENCING 子句定义。如无 REFERENCING 子句定义,缺省的引用名是 :OLD、:NEW。
触发器主要基于原生触发器进行改造兼容 Oracle DML 触发器。因此,在内部部分处理逻辑上,保持 GBase 8s 原生行为,存在以下兼容差异:
-
INSERT 的旧引用 :OLD 与DELETE的新引用 :NEW ,保持 GBase 8s 原生行为,创建触发器报错处理,返回报错信息。 Oracle 行为为置 NULL 处理。
-
GBase 8s 原生行级触发器,仅通过 FOR EACH ROW 定义,不支持以行为单位区分触发时间 (BEFORE/AFTER) 的功能。在INSERT、UPDATE、DELETE 几种触发事件下,原生 FOR EACH ROW 与 Oracle DML 触发器的对应关系如下:
GBase 8s 原生触发操作 等价对应Oracle INSERT FOR EACH ROW BEFORE INSERT FOR EACH ROW 不支持此功能 AFTER INSERT FOR EACH ROW 不支持此功能 BEFORE UPDATE FOR EACH ROW UPDATE FOR EACH ROW AFTER UPDATE FOR EACH ROW 不支持此功能 BEFORE DELETE FOR EACH ROW DELETE FOR EACH ROW AFTER DELETE FOR EACH ROW 本版本 仅语法兼容 不支持的功能分支。例如,当尝试创建 AFTER INSERT FOR EACH ROW 触发器时,系统表相关信息保持 AFTER INSERT FOR EACH ROW 。当相关 INSERT 操作激发该触发器时,实际触发时间为 BEFORE INSERT FOR EACH ROW。
-
由于以上原因,本版本 O 兼容 DML 触发器需注意以下几点操作规范:
- 如果同一张表存在多个触发器,触发器创建顺序必须保证 BEFORE FOR EACH ROW 先于 AFTER FOR EACH ROW 创建。同类型触发器执行顺序,以创建先后为准。
- 如果 BEFORE UPDATE FOR EACH ROW 触发器在触发事件 UPDATE 将 :NEW 字段值放入表中之前试图更改它,本版本执行合法,但 :NEW 引用值无法在表中生效,因为 UPDATE 操作已经完成。